// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as markedschema from "@microsoft/marked-schema";
import * as typedschema from "@microsoft/ac-typed-schema";
var fs = require("fs");
import { forEach } from "p-iteration";

export async function generateAsync() {
    // First find the top-level directory
    const relativeTopDir = findRelativeTopDirectory();
    console.log(`Using "${relativeTopDir}" as the root.`);

    await generateHostConfigAsync(relativeTopDir);
    await generateElementsAsync(relativeTopDir);
}

async function generateHostConfigAsync(relativeTopDir: string) {
    const modelObj = {
        schema: relativeTopDir + "schemas/host-config.json",
        toc: relativeTopDir + "source/nodejs/adaptivecards-site/schema-hostconfig-toc.yml",
        rootDefinition: "HostConfig"
    };

    console.log(`Generating HostConfig with:`)
    console.table(modelObj);

    let schemaModel;
    try {
        schemaModel = await markedschema.buildModel(modelObj);
    } catch (err) {
        console.error(`Error encountered while building schema model for '${modelObj.schema}':`);
        console.error(`Code: ${err.code}`);
        console.error(`Msg: ${err.message}`);
        console.error(`Stacktrace: ${err.stack}`);
    }

    let finalContents = "# Host Config";

    await forEach(schemaModel, async (root: any) => {
        await forEach(root.children, async (child: any) => {
            finalContents += "\n\n## " + child.name + "\n\n";
            finalContents += markedschema.generateMarkdown.createPropertiesSummary(child.properties, null, true, true, child.version);
        });
    });

    await writeFileAsync(relativeTopDir + "specs/HostConfig.md", finalContents);
}

async function generateElementsAsync(relativeTopDir: string) {
    const modelObj = {
        schema: relativeTopDir + "schemas/src",
        toc: relativeTopDir + "source/nodejs/adaptivecards-site/schema-explorer-toc.yml",
        rootDefinition: "AdaptiveCard",
        examplesPath: relativeTopDir + "samples/v1.*"
    };

    console.log(`Generating elements with:`);
    console.table(modelObj);

    let schemaModel;
    try {
        schemaModel = await typedschema.markdown.buildModel(modelObj);
    } catch(err) {
        console.error(`Error encountered while building elements model for '${modelObj.schema}':`);
        console.error(`Code: ${err.code}`);
        console.error(`Msg: ${err.message}`);
        console.error(`Stacktrace: ${err.stack}`);
    }

    await forEach(schemaModel, async (root: any) => {
        await forEach(root.children, async (child: any) => {
            var type: typedschema.SchemaClass = child.type;
            var markdown = typedschema.markdown.createPropertiesSummary(type, null, true, true, child.version);

            markdown = "# " + child.name + "\n\n" + markdown;

            child.properties.forEach((prop) => {
                if (typedschema.markdown.propertyHasComplexTypes(prop)) {
                    markdown += "\n\n" + typedschema.markdown.createPropertyDetails(prop, 2, null, true, true, child.version);
                }
            });

            var endAutoGeneratedTag = "<!-- END AUTO-GENERATED -->";
            markdown = "<!-- AUTO-GENERATED: This section is auto-generated from schemas/adaptive-card.json. Do NOT add anything above this or edit anything inside, it MUST be the first thing in the document and will be overwritten. -->\n\n" + markdown + endAutoGeneratedTag;

            var fileName = relativeTopDir + "specs/elements/" + child.name + ".md";
            var finalFileContents: string;
            var changed = false;

            try {

                var data = await readFileAsync(fileName, "utf8");

                var endAutoGeneratedIndex = data.indexOf(endAutoGeneratedTag);
                var currAutoGeneratedText = "";
                var currRemainingText;
                if (endAutoGeneratedIndex === -1) {
                    currAutoGeneratedText = data;
                } else {
                    endAutoGeneratedIndex += endAutoGeneratedTag.length;
                    currAutoGeneratedText = data.substring(0, endAutoGeneratedIndex);
                    currRemainingText = data.substring(endAutoGeneratedIndex);
                }

                if (markdown != currAutoGeneratedText) {
                    finalFileContents = markdown + currRemainingText;
                    changed = true;
                }

            } catch (err) {

                // No existing file, add on the ## Rendering header
                finalFileContents = markdown + "\n\n## Rendering";
                changed = true;

            }

            if (changed) {
                await writeFileAsync(fileName, finalFileContents);
            }
        });
    });
}

function readFileAsync(fileName: string, encoding: string): Promise<string> {
    return new Promise(function (resolve, reject) {
        fs.readFile(fileName, encoding, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

function writeFileAsync(fileName: string, contents: string): Promise<void> {
    return new Promise(function (resolve, reject) {
        fs.writeFile(fileName, contents, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        })
    });
}

function findRelativeTopDirectory(backLevels?: string): string {
    if (!backLevels) {
        backLevels = "";
    }

    try {
        fs.accessSync(backLevels + "schemas/1.2.0/adaptive-card.json");
        return backLevels;
    } catch (err) {
        if (backLevels.length > 50) {
            throw new Error("Couldn't find. Backlevels were: " + backLevels);
        } else {
            return findRelativeTopDirectory("../" + backLevels);
        }
    }
}
